package spring.chatroom.java_chatroom.common.datastructure;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class SessionQueue<K, V> {
    @Value("${session-queue.capacity}")
    private int DEFAULT_CAPACITY;

    //使用俩个哨兵节点，这样方便去插入
    DListNode head;
    DListNode tail;
    Map<K, DListNode> cache;//用来储存每个数据
    int capacity;
    int size;//有效数据数量

    class DListNode {

        DListNode prev;
        DListNode next;
        K key;
        V val;

        public DListNode() {

        }

        public DListNode(K key, V val) {
            this.key = key;
            this.val = val;
        }

    }

    public SessionQueue() {
        //创建哨兵链表
        head = new DListNode();
        tail = new DListNode();

        head.next = tail;
        tail.prev = head;

        size = 0;
        this.capacity = DEFAULT_CAPACITY;//默认为16
        cache = new HashMap<K, DListNode>(DEFAULT_CAPACITY);
    }

    SessionQueue(int capacity) {
        head = new DListNode();
        tail = new DListNode();

        head.next = tail;
        tail.prev = head;

        size = 0;
        this.capacity = capacity;
        cache = new HashMap<K, DListNode>(capacity);
    }

    public V getV(K key) {
        return cache.get(key).val;
    }

    //后去队首的V
    public V peekV() {
        return head.next.val;
    }

    public K peekK() {
        return head.next.key;
    }

    public K poll() {
        cache.remove(head.next.key);
        return removeHead().key;
    }

    public boolean isEmpty() {
        return cache.isEmpty();
    }

    //插入数据
    public void put(K key, V val) {
        //找到这个key对饮的链表
        DListNode node = cache.get(key);
        //不存在这个数据，那么就吧这个数据mao中后直接尾插到链表中就可以了
        if (node == null) {//不存在
            //创建这个链表
            DListNode newNode = new DListNode(key,val);
            //如果这个map的长度大于capacity，那么要删除头节点
            if (size > capacity) {
                DListNode ret = removeHead();
                cache.remove(ret.val);
            }
            //尾插入
            addToTail(newNode);
            //将这个节点放入cache中
            cache.put(key, newNode);
        } else {
            //这个数据存在，那么要吧这个数据对应的val更改,然后把这个数据放到链表最后去
            node.val = val;//改变他的值
            //将这个节点移动到最后，因为他使用过了
            removeNodeToTail(node);
        }
    }

    public V get(K key) {
        //使用过了这个数据
        removeNodeToTail(cache.get(key));

        return cache.get(key).val;
    }

    DListNode removeHead() {
        DListNode tmp = head.next;
        head.next = tmp.next;
        head.next.prev = head;

        return tmp;
    }

    @Override
    public String toString() {
        StringBuffer s = new StringBuffer();
        for (DListNode node = head.next; node.next != null; node = node.next) {
            s.append("[key = " + node.key + " val = " + node.val + "] ");
        }
        return s.toString();
    }

    //尾插
    private void removeNodeToTail(DListNode node) {
        //因为最后也会插入这个节点，所以没有从cache中移除
        //然后删除这个节点
        removeNode(node);
        //尾插这个节点，这样集移到了最后
        addToTail(node);
    }

    private void addToTail(DListNode newNode) {
        //改变next
        tail.prev.next = newNode;
        newNode.next = tail;

        //改变prev
        newNode.prev = tail.prev;
        tail.prev = newNode;

        size++;
    }

    private void removeNode(DListNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

}
